/*
 * Copyright (C) 2010 Peter Schönhofen
 *
 * This file is part of the Akkad Compiler Framework.
 *
 * The Akkad Compiler Framework is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

package hu.anaxo.akkad.processor

import scala.util.parsing.combinator.Parsers
import util.matching.Regex
import util.parsing.input.CharSequenceReader

/**
 * Customized lexer
 */

abstract class CustomLexer extends Parsers {

  // character is the parsing unit

  type Elem = Char

  // parser for end of file

  private def endFile: Parser[Token] = elem("end-of-file", _ == '\032') ^^^ EOF()

  // parser for tokens

  private def root: Parser[List[Token]] =
    rep(whitespace ~> positioned(token)) <~ (whitespace ~> endFile | err("invalid token"))

  // parser for whitespace

  protected def whitespace: Parser[Unit] = rep(elem("whitespace", _.isWhitespace)) ^^^ ()

  // parser for a single token

  protected def token: Parser[Token]

  /**
   * Create parser matching given regular expression
   *
   * @param name name
   * @param regexp regular expression
   *
   * @return parser
   */

  def regexpElem(name: String, regexp: Regex): Parser[String] =
    new Parser[String] {

      /**
       * Apply parser to given input
       *
       * @param in input
       *
       * @return matched string
       */

      def apply(in: Input) = {

        // extract source string and current offset

        val source = in.source
        val offset = in.offset

        // attempt matching

        regexp.findPrefixOf(source.subSequence(offset, source.length)) match {
          case Some(matched) =>
            Success(matched, in.drop(matched.length))
          case None =>
            Failure(name + " expected", in)
        }
      }
    }

  /**
   * Perform lexing
   *
   * @param source source
   *
   * @return tokens
   */

  def process(source: String): List[Token] =
    phrase(root) (new CharSequenceReader (source.toUpperCase)) match {
      case Success(result, _) =>
        result
      case error: NoSuccess =>
        throw Fault(error.msg, error.next.pos)
    }
}
